package com.sense.kukoo.common.media.play

import android.content.Context
import android.media.MediaPlayer
import android.net.Uri
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.text.TextUtils
import com.sense.feel.common.utils.ToastUtils
import com.sense.kukoo.common.media.Kukoo
import com.sense.kukoo.common.media.bean.LocalMediaListItemBean
import com.sense.kukoo.common.media.timer.TimerImpl
import com.sense.kukoo.common.media.watcher.UserWatcher
import com.sense.kukoo.common.repo.Repo
import com.sense.kukoo.common.utils.ApplicationContext
import com.sense.kukoo.lib_logger.LoggerManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch

class MediaPlayImpl {

    private val TAG: String = javaClass.simpleName

    private var mPlayer: MediaPlayer = MediaPlayer()
    private var currentPlay:LocalMediaListItemBean? = null
    private var currentProcess:Int = 0
    private var errorPlay:LocalMediaListItemBean? = null
    private var errorProcess:Int = -1
    private var userProgress:Int = 0
    private var lastUploadProcess:Long = 0
    private var currentState = UserWatcher.MusicStateWatcher.STATE_NOT_PLAYING
    private var userState = UserWatcher.MusicStateWatcher.STATE_NOT_PLAYING
    private var errorState = UserWatcher.MusicStateWatcher.STATE_ERROR_RETRYING
    private var flag_seekOnPause = false
    private var currentPercent:Int = 1
    private var flag_isPrepared = false

    private var isSeeking = false

    val lintener1 = object : MediaPlayer.OnBufferingUpdateListener {
        override fun onBufferingUpdate(mp: MediaPlayer?, percent: Int) {
            LoggerManager.d(TAG,"OnBufferingUpdateListener.onBufferingUpdate percent=${percent}")
        }
    }
    val lintener2 = object : MediaPlayer.OnSeekCompleteListener {
        override fun onSeekComplete(mp: MediaPlayer?) {
            isSeeking = false
            LoggerManager.e(TAG,"OnSeekCompleteListener.onSeekComplete currentState=${currentState} ,userState=${userState}")
            //currentState=STATE_SEEKING ,userState=STATE_PAUSE 在SEEKING状态点击pause
            //currentState=STATE_PLAYING ,userState=-1
            //currentState=STATE_SEEKING ,userState=STATE_PLAYING 在SEEKING状态点击pause后再点击start
            //currentState=STATE_SEEKING ,userState=STATE_SEEKING 在SEEKING状态再次SEEKING
            when (userState) {
                UserWatcher.MusicStateWatcher.STATE_SEEKING,
                UserWatcher.MusicStateWatcher.STATE_PLAYING ->{
                    //播放状态拖动
                    currentState = UserWatcher.MusicStateWatcher.STATE_PLAYING
                    Kukoo.watcher.dispatchEvent {
                        onPlayStateChanged(currentState)
                    }
                }
                UserWatcher.MusicStateWatcher.STATE_PAUSE ->{
                    //暂停状态拖动
                    /*currentState = UserWatcher.MusicStateWatcher.STATE_PAUSE
                    Kukoo.watcher.dispatchEvent {
                        onPlayStateChanged(currentState)
                    }*/
                    LoggerManager.e(TAG, "暂停状态拖动")
                    currentState = UserWatcher.MusicStateWatcher.STATE_PLAYING
                    pause()
                }
                /*UserWatcher.MusicStateWatcher.STATE_SEEKING ->{

                }*/
                else ->{
                    LoggerManager.e(TAG, "onSeekComplete 异常状态？")
                }
            }
        }
    }
    val lintener3 = object : MediaPlayer.OnCompletionListener {
        override fun onCompletion(mp: MediaPlayer?) {
            LoggerManager.d(TAG,"OnCompletionListener.onCompletion")
            onCompletion()
        }
    }
    val lintener4 = object : MediaPlayer.OnVideoSizeChangedListener {
        override fun onVideoSizeChanged(mp: MediaPlayer?, width: Int, height: Int) {
            LoggerManager.d(TAG,"OnVideoSizeChangedListener.onVideoSizeChanged width=${width}, height=${height}")
        }
    }
    val lintener5 = object : MediaPlayer.OnErrorListener {
        override fun onError(mp: MediaPlayer?, what: Int, extra: Int): Boolean {
            LoggerManager.e(TAG,"OnErrorListener.onError what=${what}, extra=${extra} currentState=${currentState}, userState=${userState}")
            when (what) {
                MediaPlayer.MEDIA_ERROR_UNKNOWN -> {
                    LoggerManager.e(TAG, "未知异常，？？？")
                }
                else -> {}
            }
            return true
        }
    }
    val lintener6 = object : MediaPlayer.OnInfoListener {
        override fun onInfo(mp: MediaPlayer?, what: Int, extra: Int): Boolean {
            LoggerManager.e(TAG,"OnInfoListener.onInfo what=${what}, extra=${extra}")
            when (what) {
                MediaPlayer.MEDIA_INFO_BUFFERING_START -> {
                    LoggerManager.e(TAG,"OnInfoListener.onInfo BUFFERING_START !")
                }
                MediaPlayer.MEDIA_INFO_BUFFERING_END -> {
                    LoggerManager.e(TAG,"OnInfoListener.onInfo BUFFERING_END !")
                }
                else -> {}
            }

            return false
        }
    }
    val lintener7 = object : MediaPlayer.OnPreparedListener {
        override fun onPrepared(mp: MediaPlayer?) {
            //STATE_PLAYING -1
            LoggerManager.e(TAG,"OnPreparedListener.onPrepared currentState=${currentState}, userState=${userState}")
            flag_isPrepared = true
            //
            if(errorProcess>0){
                mPlayer.seekTo(errorProcess)
                isSeeking = true
                errorProcess = -1
            }

            when (userState) {
                UserWatcher.MusicStateWatcher.STATE_NOT_PLAYING ->{

                }
                UserWatcher.MusicStateWatcher.STATE_PAUSE ->{
                    //PREPARE -> PAUSE
                    currentState = UserWatcher.MusicStateWatcher.STATE_PAUSE
                    userState = -1
                }
                UserWatcher.MusicStateWatcher.STATE_ERROR_RETRYING ->{

                }
                else ->{
                    start()
                }
            }
        }
    }

    init {
        mPlayer.setOnBufferingUpdateListener(lintener1)
        mPlayer.setOnSeekCompleteListener(lintener2)
        mPlayer.setOnCompletionListener(lintener3)
        mPlayer.setOnVideoSizeChangedListener(lintener4)
        mPlayer.setOnErrorListener(lintener5)
        mPlayer.setOnInfoListener(lintener6)
        mPlayer.setOnPreparedListener(lintener7)
    }

    fun play(bookItems: LocalMediaListItemBean){
        LoggerManager.e(TAG, "play() ：currentState=${currentState}, userState=${userState}")
        if(currentPlay != null
            && currentPlay?.idOfBooks() == bookItems.idOfBooks()
            && currentPlay?.idOfChapter() == bookItems.idOfChapter()){
            //相同的
            LoggerManager.e(TAG, "相同的音乐，继续播放")
            when (currentState) {
                UserWatcher.MusicStateWatcher.STATE_NOT_PLAYING -> {
                    LoggerManager.e(TAG, "currentState=${currentState} 异常状态？")
                }
                UserWatcher.MusicStateWatcher.STATE_ERROR_RETRYING,
                UserWatcher.MusicStateWatcher.STATE_PREPARE,
                UserWatcher.MusicStateWatcher.STATE_PLAYING,
                UserWatcher.MusicStateWatcher.STATE_SEEKING -> {
                    //正常状态, do nothing
                }
                UserWatcher.MusicStateWatcher.STATE_PAUSE -> {
                    start()
                }
                else -> {}
            }
            return
        }
        //
        Kukoo.timer.onPlayNew()
        //上报上一首的播放进度
        currentPlay?.let {
            val total = currentProcess.toLong()
            uploadProcess(it, total, false)
        }
        //设置初始状态
        errorPlay = null
        currentPlay = bookItems
        currentState = UserWatcher.MusicStateWatcher.STATE_PREPARE
        userState = -1
        userProgress = -1
        errorProcess = -1
        //上报
        lastUploadProcess = 1000L //毫秒值，上报服务器时最低1秒
        uploadProcess(bookItems, lastUploadProcess, false)
        //
        if(errorProcess <= 0){
            //不是通过异常重启执行的播放
            val time = ApplicationContext.getString(com.sense.kukoo.common_i18n.R.string.media_player_time_zero)
            currentPercent = 1
            Kukoo.watcher.dispatchEvent{
                onMusicChanged(bookItems)
                onPlayTimeChanged(0, 0,time, time)
                onPlayPercentChanged(currentPercent)
                onPlayStateChanged(currentState)
            }
        }
        //
        try {
            flag_isPrepared = false
            mPlayer.reset()
            invokeMusicDataSource(mPlayer, bookItems)
            mPlayer.prepareAsync()
        }catch (e:Exception){
            handleException(e)
        }
    }

    fun start(){
        //STATE_PREPARE STATE_PAUSE
        //STATE_SEEKING STATE_PAUSE
        //STATE_SEEKING STATE_PLAYING
        LoggerManager.e(TAG, "start() ：currentState=${currentState}, userState=${userState}")
        when (currentState) {
            UserWatcher.MusicStateWatcher.STATE_ERROR_RETRYING ->{
                errorState = UserWatcher.MusicStateWatcher.STATE_PREPARE
                Kukoo.watcher.dispatchEvent {
                    onPlayStateChanged(errorState)
                }
                return
            }
            UserWatcher.MusicStateWatcher.STATE_NOT_PLAYING,
            UserWatcher.MusicStateWatcher.STATE_SEEKING,
            UserWatcher.MusicStateWatcher.STATE_PLAYING ->{
                //
                userState = UserWatcher.MusicStateWatcher.STATE_PLAYING
                Kukoo.watcher.dispatchEvent {
                    onPlayStateChanged(userState)
                }
                return
            }
            UserWatcher.MusicStateWatcher.STATE_PREPARE ->{
                if(userState == UserWatcher.MusicStateWatcher.STATE_PAUSE){
                    userState = -1
                    Kukoo.watcher.dispatchEvent {
                        onPlayStateChanged(UserWatcher.MusicStateWatcher.STATE_PREPARE)
                    }
                    return
                }
            }
            UserWatcher.MusicStateWatcher.STATE_PAUSE ->{
                //正常状态
            }
            else ->{}
        }
        try {
            setPlayerSpeed()
            mPlayer.start()
        }catch (e:Exception){
            handleException(e)
        }
        userState = -1
        currentState = UserWatcher.MusicStateWatcher.STATE_PLAYING
        Kukoo.watcher.dispatchEvent {
            onPlayStateChanged(currentState)
        }
        setLoop(true)
    }

    fun pause(){
        //
        LoggerManager.e(TAG, "pause() ：currentState=${currentState}, userState=${userState}")
        when (currentState) {
            UserWatcher.MusicStateWatcher.STATE_ERROR_RETRYING ->{
                errorState = UserWatcher.MusicStateWatcher.STATE_PAUSE
                Kukoo.watcher.dispatchEvent {
                    onPlayStateChanged(errorState)
                }
                return
            }
            UserWatcher.MusicStateWatcher.STATE_PLAYING ->{
                //正常状态 play->pause
            }
            UserWatcher.MusicStateWatcher.STATE_PREPARE,
            UserWatcher.MusicStateWatcher.STATE_SEEKING ->{
                userState = UserWatcher.MusicStateWatcher.STATE_PAUSE
                Kukoo.watcher.dispatchEvent {
                    onPlayStateChanged(userState)
                }
                return
            }
            else ->{
                LoggerManager.e(TAG, "异常状态？")
                return
            }
        }
        setLoop(false)
        try {
            if (mPlayer.isPlaying) {
                mPlayer.pause()
            }
            //上报数据
            currentPlay?.let {
                val total = currentProcess.toLong()
                uploadProcess(it, total, false)
                lastUploadProcess = total
            }
        }catch (e:Exception){
            handleException(e)
        }
        //
        userState = -1
        currentState = UserWatcher.MusicStateWatcher.STATE_PAUSE
        Kukoo.watcher.dispatchEvent {
            onPlayStateChanged(currentState)
        }
    }

    //停止播放，回到not playing的初始状态
    fun stop(){
        LoggerManager.e(TAG, "stop() ：currentState=${currentState}, userState=${userState}")
        setLoop(false)
        try {
            if (mPlayer.isPlaying) {
                mPlayer.pause()
            }
            mPlayer.reset()
        }catch (e:Exception){
            handleException(e)
        }
        currentState = UserWatcher.MusicStateWatcher.STATE_NOT_PLAYING
        currentPlay = null
        Kukoo.watcher.dispatchStopPlay()
    }

    fun seekTo(userProgress:Int){
        LoggerManager.e(TAG, "seekTo() ：currentState=${currentState}, userState=${userState}, userProgress=${userProgress}")
        if (!flag_isPrepared) {
            LoggerManager.e(TAG, "flag_isPrepared=${flag_isPrepared}, 未Prepared时不能seek, 不要调用mPlayer.duration")
            return
        }
        when (currentState) {
            UserWatcher.MusicStateWatcher.STATE_ERROR_RETRYING ->{
                errorProcess = userProgress
                return
            }
            UserWatcher.MusicStateWatcher.STATE_PREPARE,
            UserWatcher.MusicStateWatcher.STATE_NOT_PLAYING ->{
                //?
                return
            }
            UserWatcher.MusicStateWatcher.STATE_PAUSE,
            UserWatcher.MusicStateWatcher.STATE_SEEKING,
            UserWatcher.MusicStateWatcher.STATE_PLAYING ->{
                //正常状态
            }
            else ->{
                return
            }
        }
        if(currentState!=UserWatcher.MusicStateWatcher.STATE_SEEKING){
            userState = currentState
            currentState = UserWatcher.MusicStateWatcher.STATE_SEEKING
            Kukoo.watcher.dispatchEvent {
                onPlayStateChanged(currentState)
            }
        }
        try {
            mPlayer.seekTo(userProgress)
            isSeeking = true
            //
            var duration = mPlayer.duration
            duration = if(duration <= 0) 0 else duration
            val currentTime = calculateTime(userProgress)
            val totalTime = calculateTime(duration)
            //非播放状态时，刷新进度
            if (!mPlayer.isPlaying){
                Kukoo.watcher.dispatchEvent {
                    onPlayTimeChanged(userProgress, duration, currentTime, totalTime)
                }
                //
                val percent = (currentProcess*99)/(duration+1)
                setPercent(percent)
            }
        }catch (e:Exception){
            handleException(e)
        }
    }

    fun seekBy(milliseconds:Int){
        if (!flag_isPrepared) {
            LoggerManager.e(TAG, "flag_isPrepared=${flag_isPrepared}, 未Prepared时不能seek, 不要调用mPlayer.duration")
            return
        }
        val duration = mPlayer.duration
        var current = mPlayer.currentPosition
        current += milliseconds
        current = if(current < 0) 0 else current
        current = if(current > duration) duration else current
        seekTo(current)
    }

    fun setSpeed(speed:Float){
        LoggerManager.d(TAG, "setSpeed speed=${speed}")
        Repo.speed.set(speed)
        //
        Kukoo.watcher.dispatchEvent {
            onPlaySpeedChanged(speed)
        }
        try {
            if (mPlayer.isPlaying) {
                setPlayerSpeed()
            }
        }catch (e:Exception){
            handleException(e)
        }
    }

    private fun setPlayerSpeed(){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            val playbackParams = mPlayer.playbackParams
            playbackParams.speed = Repo.speed.get()
            mPlayer.playbackParams = playbackParams
        }
    }

    private fun invokeMusicDataSource(player: MediaPlayer, bookItems: LocalMediaListItemBean){
        val path = bookItems.getAudioUrl()
        if(TextUtils.isEmpty(path)){
            LoggerManager.e(TAG, "audioUrl is Empty !")
            return
        }
        /*if (bookItems.type==BookBean.TYPE_PODCAST) {
            LoggerManager.d("bookItems.type==BookBean.TYPE_PODCAST")
            val context = ApplicationContext.instance()
            val uri = Uri.parse(path)
            val headers = HashMap<String, String>()
            headers.put("referer","https://feed.podbean.com")
            headers.put("User-Agent","Mozilla/5.0 (Linux; Android 10; WLZ-AN00 Build/HUAWEIWLZ-AN00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Mobile Safari/537.36")
            //
            val method: Method = player.javaClass.getMethod("setDataSource",
                                                            Context::class.java,
                                                            Uri::class.java,
                                                            MutableMap::class.java )
            method.invoke(player, context, uri, headers)
            //player.setAudioStreamType(AudioManager.STREAM_MUSIC)
        }else{
            player.setDataSource(path)
        }*/
        player.setDataSource(path)
    }

    private fun onCompletion(){
        LoggerManager.e(TAG, "onCompletion() ：currentState=${currentState}")
        var flag = false
        //上报数据
        currentPlay?.let {
            val total = currentProcess.toLong()
            uploadProcess(it, total, true)
            lastUploadProcess = total
        }
        //
        if (!flag) {
            //计时器
            if (Kukoo.timer.isWhenCurrentEpisodeEnd()) {
                Kukoo.timer.setTimer(TimerImpl.TYPE_OFF)
                //在当前内容上暂停
                val duration = mPlayer.duration
                seekTo(duration - 500)
                pause()
                flag = true
            }
        }
        //播放下一首
        if (!flag){
            Kukoo.playlist.onMusicCompletion(currentPlay!!)
        }
    }

    fun getCurrentPlayMusic():LocalMediaListItemBean? = currentPlay

    private var mLoop = false
    private var mDelayedTime = 160L
    private val mHandler = Handler(Looper.getMainLooper())
    private val mRunnable:Runnable = object :Runnable{
        override fun run() {
            if(mLoop){
                refreshState()
            }
        }
    }

    private val mErrorRunnable:Runnable = object :Runnable{
        override fun run() {
            val process = errorProcess
            val error = errorPlay
            errorPlay = null
            if(error == null || currentPlay != errorPlay){
                return
            }
            play(error)
            if(errorState!=UserWatcher.MusicStateWatcher.STATE_ERROR_RETRYING){
                userState = errorState
            }
            errorState = -1
            errorProcess = process
        }
    }

    private fun setLoop(isLoop:Boolean){
        LoggerManager.d(TAG,"setLoop=${isLoop}")
        if (mLoop != isLoop) {
            mLoop = isLoop
            if(mLoop){
                calculateLoopTime()
                mHandler.postDelayed(mRunnable, mDelayedTime)
            }else{
                mHandler.removeCallbacks(mRunnable)
            }
        }
    }

    private fun refreshState(){

        if (mPlayer.isPlaying){
            var duration = mPlayer.duration
            duration = if(duration <= 0) 0 else duration
            currentProcess = mPlayer.currentPosition
            val currentTime = calculateTime(currentProcess)
            val totalTime = calculateTime(duration)
            Kukoo.watcher.dispatchEvent {
                onPlayTimeChanged(currentProcess, duration, currentTime, totalTime)
            }
            //
            val percent = (currentProcess*99)/(duration+1)
            setPercent(percent)
        }
        mHandler.postDelayed(mRunnable, mDelayedTime)
    }

    private fun setPercent(p:Int){
        var per = if(p <=1 ) 1 else p
        per = if(per >= 99) 99 else per
        if(currentPercent != per){
            currentPercent = per
            Kukoo.watcher.dispatchEvent {
                onPlayPercentChanged(currentPercent)
            }
        }
    }

    private fun calculateLoopTime(){
        val duration = mPlayer.duration
        var delayedTime = duration/400
        delayedTime = if(delayedTime < 16) 16 else delayedTime
        delayedTime = if(delayedTime > 320) 320 else delayedTime
        mDelayedTime = delayedTime.toLong()
        LoggerManager.d(TAG,"calculateLoopTime -> ${delayedTime}")
    }

    private fun calculateTime(milliseconds:Int): String {
        val time: Int = milliseconds / 1000

        val minute: Int
        val second: Int
        return if (time >= 60) {
            minute = time / 60
            second = time % 60
            (if (minute < 10) "0$minute" else "" + minute) + if (second < 10) ":0$second" else ":$second"
        } else {
            second = time
            if (second < 10) {
                "00:0$second"
            } else "00:$second"
        }
    }

    private fun handleException(e:Exception){
        LoggerManager.e(TAG,"播放器发生了异常！！！ currentState=${currentState}")
        e.printStackTrace()
        //提示
        mHandler.post {
            ToastUtils.showToast(ApplicationContext.getString(com.sense.kukoo.common_i18n.R.string.common_hint_play_error))
        }
        //
        currentState = UserWatcher.MusicStateWatcher.STATE_ERROR_RETRYING
        Kukoo.watcher.dispatchEvent {
            onPlayStateChanged(currentState)
        }
        errorProcess = currentProcess
        errorPlay = currentPlay
        errorState = UserWatcher.MusicStateWatcher.STATE_ERROR_RETRYING
        mHandler.postDelayed(mErrorRunnable, 500)
    }

    private fun uploadProcess(bookItems: LocalMediaListItemBean, total: Long, isEnding:Boolean){
        if(total >= 1000){
            uploadProcess(bookItems.idOfChapter(), bookItems.idOfBooks(), total, isEnding)
        } else {
            LoggerManager.e(TAG, "uploadProcess() total=${total}, 不上报")
        }
    }

    private fun uploadProcess(
        contentId: Long,
        contentUserId: Long,
        total: Long,
        isEnding:Boolean
    ){
        GlobalScope.launch(Dispatchers.IO){
            try {
                //	total 收听时长(单位: 秒)
                Repo.http.saveListen(contentId, contentUserId, (total/1000), isEnding)
            }catch (e:java.lang.Exception){
                e.printStackTrace()
            }
        }
    }
}